﻿Imports System.Collections

Public Class Shuffler(Of T As Class)

  ''' <summary>
  ''' returns the list as a shuffled copy
  ''' </summary>
  Public Overridable Function Shuffle(ByVal listToShuffle As IList(Of T)) As IList(Of T)
    'copy into an array
    Dim indexArray(listToShuffle.Count - 1) As T
    For i As Integer = 0 To indexArray.Length - 1
      indexArray(i) = listToShuffle(i)
    Next

    'shuffle it
    For i As Integer = 0 To 10
      Try
        Array.Sort(indexArray, New ShuffleComparer(100 * indexArray.Length))
        Exit For
      Catch ex As Exception
        'TODO: use another shuffle method.
        '*sometimes* a bogus icomparer exception occurs:
        'apparently the icomparer way is not the way to shuffle. 

        'retry a few times
        If i = 10 Then
          Throw ex
        End If
      End Try
    Next

    'return as list
    Return indexArray.ToList()
  End Function

  ''' <summary>
  ''' pseudo-comparer class, which returns random results for the compare
  ''' until it decides it has finished its shuffling.
  ''' </summary>
  Private Class ShuffleComparer
    Implements IComparer(Of T)

    Private shuffleStrength As Integer
    Private numberOfShufflesDone As Integer = 0
    Private random As New Random(Now.Millisecond)

    ''' <summary>
    ''' constructor. SHufflestrength is the number of times a compare result returns 
    ''' a random result
    ''' </summary>
    Public Sub New(ByVal shuffleStrength As Integer)
      Me.shuffleStrength = shuffleStrength
    End Sub

    ''' <summary>
    ''' returns -1 or 1 randomly to shuffle a collection.
    ''' </summary>
    Public Function Compare(ByVal x As T, ByVal y As T) As Integer Implements System.Collections.Generic.IComparer(Of T).Compare
      If Me.numberOfShufflesDone > Me.shuffleStrength Then
        'done shuffling
        Return 0
      Else
        If y Is x Then
          Return 0
        End If

        Dim randomNumber = 0
        While randomNumber = 0
          randomNumber = Me.random.Next(-1, 2) '-1, 0 or 1.
        End While

        Me.numberOfShufflesDone += 1
        Return randomNumber
      End If
    End Function

  End Class

End Class
